# SPDX-FileCopyrightText: 2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# SPDX-FileContributor: Andrew Hayzen <andrew.hayzen@kdab.com>
# SPDX-FileContributor: Gerhard de Clercq <gerhard.declercq@kdab.com>
#
# SPDX-License-Identifier: MIT OR Apache-2.0

# ANCHOR: book_cmake_setup
cmake_minimum_required(VERSION 3.24)

project(example_qml_minimal)
set(APP_NAME ${PROJECT_NAME})

# Rust always links against non-debug Windows runtime on *-msvc targets
# Note it is best to set this on the command line to ensure all targets are consistent
# https://github.com/corrosion-rs/corrosion/blob/master/doc/src/common_issues.md#linking-debug-cc-libraries-into-rust-fails-on-windows-msvc-targets
# https://github.com/rust-lang/rust/issues/39016
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
  set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")
endif()

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

if(NOT USE_QT5)
    find_package(Qt6 COMPONENTS Core Gui Qml QuickControls2 QmlImportScanner)
endif()
if(NOT Qt6_FOUND)
    find_package(Qt5 5.15 COMPONENTS Core Gui Qml QuickControls2 QmlImportScanner REQUIRED)
endif()
# ANCHOR_END: book_cmake_setup

# ANCHOR: book_cmake_find_qmake
# The path to the qmake executable path needs to be passed to the Rust
# library's build script to ensure it uses the same installation of Qt as CMake.
get_target_property(QMAKE Qt::qmake IMPORTED_LOCATION)
# ANCHOR_END: book_cmake_find_qmake

# ANCHOR: book_cmake_find_corrosion
find_package(Corrosion QUIET)
if(NOT Corrosion_FOUND)
    include(FetchContent)
    FetchContent_Declare(
        Corrosion
        GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
        GIT_TAG v0.4.2
    )

    FetchContent_MakeAvailable(Corrosion)
endif()
# ANCHOR_END: book_cmake_find_corrosion

# ANCHOR: book_cmake_use_corrosion
set(CRATE qml-minimal)
# Corrosion creates a CMake target with the same name as the crate.
corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml CRATES ${CRATE})

# The Rust library's build script needs to be told where to output the
# generated headers so CMake can find them. To do this, tell Corrosion
# to set the CXXQT_EXPORT_DIR environment variable when calling `cargo build`.
# Also, set the QMAKE environment variable to ensure the Rust library uses
# the same installation of Qt as CMake.
set(CXXQT_EXPORT_DIR "${CMAKE_CURRENT_BINARY_DIR}/cxxqt")
corrosion_set_env_vars(${CRATE}
    "CXXQT_EXPORT_DIR=${CXXQT_EXPORT_DIR}"
    "QMAKE=${QMAKE}"
)

# Create an INTERFACE library target to link libraries to and add include paths.
# Linking this to both the application and the tests avoids having to setup
# the include paths and linked libraries for both of those.
add_library(${APP_NAME}_lib INTERFACE)

# Include the headers generated by the Rust library's build script. Each
# crate gets its own subdirectory under CXXQT_EXPORT_DIR. This allows you
# to include headers generated by multiple crates without risk of one crate
# overwriting another's files.
target_include_directories(${APP_NAME}_lib INTERFACE "${CXXQT_EXPORT_DIR}/${CRATE}")

target_link_libraries(${APP_NAME}_lib INTERFACE
    # WHOLE_ARCHIVE is needed for the generated QML plugin to register on startup,
    # otherwise the linker will discard the static variables that initialize it.
    "$<LINK_LIBRARY:WHOLE_ARCHIVE,${CRATE}-static>"
    Qt::Core
    Qt::Gui
    Qt::Qml
    Qt::QuickControls2
)
# ANCHOR_END: book_cmake_use_corrosion

# ANCHOR: book_cmake_executable
# Define the executable with the C++ source
add_executable(${APP_NAME} cpp/main.cpp)

# Link to the Rust library
target_link_libraries(${APP_NAME} PRIVATE ${APP_NAME}_lib)

# If we are using a statically linked Qt then we need to import any qml plugins
qt_import_qml_plugins(${APP_NAME})
# ANCHOR_END: book_cmake_executable

#
# Unit test
#

if(TARGET Qt6::Core)
    find_package(Qt6 COMPONENTS QuickTest Test REQUIRED)
else()
    find_package(Qt5 COMPONENTS QuickTest Test REQUIRED)
endif()

function(add_qml_test TEST_NAME)
    set(APP_TEST_NAME ${APP_NAME}_${TEST_NAME}_test)
    add_executable(${APP_TEST_NAME} tests/${TEST_NAME}/tst_${TEST_NAME}.cpp)
    target_link_libraries(${APP_TEST_NAME} PRIVATE
        ${APP_NAME}_lib
        Qt::QuickTest
    )
    qt_import_qml_plugins(${APP_TEST_NAME})

    set(TEST_CMD
        $<TARGET_FILE:${APP_TEST_NAME}> -input
        ${CMAKE_CURRENT_SOURCE_DIR}/tests/${TEST_NAME}/tst_${TEST_NAME}.qml
    )

    add_test(
        NAME ${APP_TEST_NAME}
        COMMAND ${TEST_CMD}
    )

    # RUNTIME_ENV comes from the CMakeLists.txt at the root of this repository.
    set_tests_properties(
        ${APP_NAME}_${TEST_NAME}_test PROPERTIES ENVIRONMENT_MODIFICATION
                                                 "${RUNTIME_ENV}"
    )

    add_valgrind_test(
        ${APP_TEST_NAME} "${TEST_CMD}" ${CMAKE_CURRENT_BINARY_DIR}
    )
endfunction()

add_qml_test(myobject)
